package org.ddevil.data.set;

import java.util.Iterator;
import java.util.List;

import org.ddevil.data.ChangeTracker;
import org.ddevil.data.Data;
import org.ddevil.data.ChangeTracker.Change;
import org.ddevil.data.util.DataSetIterator;


/**
 * A TrackableDataSet implementation that wraps another DataSet and tracks
 * any changes that are made to it through this class.
 *
 * <p>
 * This is a {@link org.ddevil.data.set.DataSetWrapper} implementation.
 * If the DataSet being wrapped is changed without this class knowing about
 * it then the results will be undefined.
 *
 * @see org.ddevil.data.set.DataSetWrapper
 *
 * @author Rich O'Connell
 * @author Eric Lundin
 */
public class BasicTrackableDataSet<T extends Data> implements TrackableDataSet<T>, DataSetWrapper {

	private static final long serialVersionUID = -6174072032243792141L;

	/** The model backing, the DataSet whose changes we will be tracking. */
    protected DataSet<T> model;

    /** Flag indicating if we should be tracking changes or not. */
    protected boolean trackChanges;

    /** The ChangeTracker which will control the change tracking. */
    protected ChangeTracker tracker;

    /**
     * Create a new TrackableDataSet that will wrap the given data set
     * and track any changes made to it via this new TrackableDataSet.
     *
     * @param model
     *          The DataSet to wrap.
     */
    public BasicTrackableDataSet(DataSet<T> model) {
        this.model = model;
        trackChanges = true;
        tracker = new ChangeTracker();
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.TrackableDataSet#trackChanges(boolean)
     */
    public void trackChanges(boolean boo) {
        this.trackChanges = boo;
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.TrackableDataSet#clearChanges()
     */
    public void clearChanges() {
        tracker.clear();
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.TrackableDataSet#getChanges()
     */
    public List<Change> getChanges() {
        return tracker.getChanges();
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#importRecord(org.ddevil.data.Data)
     */
    public void importRecord(T inData) {
        if(trackChanges){
            tracker.itemAdded(inData);
        }
        model.importRecord(inData);

    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#importRecords(org.ddevil.data.DataSet)
     */
    public void importRecords(DataSet<T> inData) {
        if(trackChanges){
            for(int i = 0; i < inData.size(); i++){
                importRecord(inData.exportRecord(i));
            }
        }else{
            model.importRecords(inData);
        }

    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#setDataAt(int, java.lang.String, java.lang.Object)
     */
    public void setDataAt(int rowIndex, String dbIdentifier, Object data) {
        if(trackChanges){
            Data old = model.exportRecord(rowIndex);
            Data nuu = model.exportRecord(rowIndex);
            nuu.setDataItem(dbIdentifier, data);
            tracker.itemModified(old, nuu);
        }
        model.setDataAt(rowIndex, dbIdentifier, data);

    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#removeRecord(int)
     */
    public void removeRecord(int index) {
        if(trackChanges){
            tracker.itemRemoved(model.exportRecord(index));
        }
        model.removeRecord(index);
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#clear()
     */
    public void clear() {
        if(trackChanges){
            for(int i = 0; i < model.size(); i++){
                tracker.itemRemoved(model.exportRecord(i));
            }
        }
        model.clear();
    }

    /*
     *  (non-Javadoc)
     * @see org.ddevil.data.DataSet#exportRecord(int)
     */
    public T exportRecord(int rowNum) {
        return model.exportRecord(rowNum);
    }

    /**
     * {@inheritDoc}
     */
    public DataSet exportRecords(int... indices) {
        return model.exportRecords(indices);
    }

    /**
     * {@inheritDoc}
     */
    public Object getDataAt(int rowIndex, String dbIdentifier) {
        return model.getDataAt(rowIndex, dbIdentifier);
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getIdentifiers() {
       return model.getIdentifiers();
    }

    /**
     * {@inheritDoc}
     */
    public int size() {
        return model.size();
    }

    /**
     * {@inheritDoc}
     */
    public DataSet getWrappedDataSet() {
        return model;
    }

    /**
	 * {@inheritDoc}
	 */
	public Iterator<T> iterator() {
		return new DataSetIterator<T>(this);
	}

}
